package me.stone.stanimeclient.core.ui.adapters.refresh

import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
import java.lang.ref.SoftReference
import java.util.LinkedList
import kotlin.math.min

object PreInflaterHelper {

    fun get(): PreInflaterHelper {
        return this
    }

    private const val DEFAULT_PRELOAD_COUNT = 5

    private val viewCache = ViewCache()

    private var layoutInflater: ILayoutInflater = DefaultAsyncLayoutInflater.get()

    fun preloadOnce(parent: ViewGroup, layoutId: Int, maxCount: Int = DEFAULT_PRELOAD_COUNT) {
        preload(parent, layoutId, maxCount, 1)
    }

    fun preload(parent: ViewGroup, layoutId: Int, maxCount: Int, forcePreCount: Int = 0) {
        val viewsAvailableCount = viewCache.getViewPoolAvailableCount(layoutId)
        if (viewsAvailableCount > maxCount) {
            // 当前缓存的View已大于需求，直接退出
            return
        }
        var needPreloadCount = maxCount - viewsAvailableCount
        if (forcePreCount > 0) {
            // 取强制要求加载的数量和需要加载的数量最小值
            needPreloadCount = min(forcePreCount, needPreloadCount)
        }
        for (i in 0..< needPreloadCount) {
            preAsyncInflateView(parent, layoutId)
        }
    }

    fun preAsyncInflateView(parent: ViewGroup, layoutId: Int) {
        layoutInflater.asyncInflateView(parent, layoutId, object : InflateCallback {
            override fun onInflateFinished(layoutId: Int, view: View) {
                viewCache.putView(layoutId, view)
            }
        })
    }

    fun getView(parent: ViewGroup, layoutId: Int, maxCount: Int = DEFAULT_PRELOAD_COUNT): View {
        val view = viewCache.getView(layoutId)
        if (view != null) {
            preload(parent, layoutId, maxCount)
            return view
        }
        return layoutInflater.inflate(parent, layoutId)
    }

    fun setAsyncInflater(asyncInflater: ILayoutInflater): PreInflaterHelper {
        this.layoutInflater = asyncInflater
        return this
    }

    private class ViewCache {

        private val viewPools: SparseArray<LinkedList<SoftReference<View?>>> = SparseArray()

        fun getViewPool(layoutId: Int): LinkedList<SoftReference<View?>> {
            var views = viewPools.get(layoutId)
            if (views == null) {
                views = LinkedList()
                viewPools.put(layoutId, views)
            }
            return views
        }

        fun getViewPoolAvailableCount(layoutId: Int): Int {
            val views = getViewPool(layoutId)
            val it = views.iterator()
            var count = 0
            while (it.hasNext()) {
                if (it.next().get() != null) {
                    count += 1
                } else {
                    it.remove()
                }
            }
            return count
        }

        fun putView(layoutId: Int, view: View?) {
            view?.let {
                getViewPool(layoutId).offer(SoftReference(view))
            }
        }

        fun getView(layoutId: Int): View? {
            return getViewFromPool(getViewPool(layoutId))
        }

        private fun getViewFromPool(views: LinkedList<SoftReference<View?>>): View? {
            if (views.isEmpty()) {
                return null
            }
            val target = views.pop().get() ?: return getViewFromPool(views)
            return target
        }

    }

    interface InflateCallback {
        fun onInflateFinished(layoutId: Int, view: View)
    }

    interface ILayoutInflater {

        fun inflate(parent: ViewGroup, layoutId: Int): View

        fun asyncInflateView(parent: ViewGroup, layoutId: Int, callback: InflateCallback)

    }

}
